/**
 * @name    ObjectExtend
 * @author  GinKo.Wang
 * @mail    <a href='mailTo:yingosen@gmaiil.com'>GinKo.Wang</a>
 * @date    2015-11-27 15:25
 *
 */
(function (window) {
    var ObjectExtend = function () {
    };
    ObjectExtend.prototype = {
        extend: function (objects) {
            var _target = {};
            var len = objects.length;
            if (len == 0) {
                return _target;
            }
            var _prototype;
            for (var i = 0; i < len; i++) {
                var _item = objects[i];
                if (typeof(_item) !== "object") {
                    continue;
                }
                for (_prototype in _item) {
                    _target[_prototype] = _item[_prototype];
                }
            }
            return _target;
        }
    };
    var oe = new ObjectExtend();
    window.extend = function () {
        return oe.extend(Array.prototype.slice.call(arguments));
    };
})(window);
/**
 * @name    RequestMessage
 * @author  GinKo
 * @mail    <a href='mailTo:yingosen@gmaiil.com'>GinKo.Wang</a>
 * @date    2015-11-10 17:22
 *
 */
var RequestMessage = function (si, params) {
    this.si = si;
    this.ri = "0";
    this.params = window.extend({}, params);
    this.date = 0;
};
/**
 * @name    RequestMessageModel
 * @author  GinKo
 * @mail    <a href='mailTo:yingosen@gmaiil.com'>GinKo.Wang</a>
 * @date    2015-11-10 17:22
 *
 */
var RequestMessageModel = function (request, callback, errorCallback) {
    this.request = request;
    this.callback = callback;
    this.errorCallback = errorCallback;
};
/**
 * @name    RequestMessageCallback
 * @author  GinKo
 * @mail    <a href='mailTo:yingosen@gmaiil.com'>GinKo.Wang</a>
 * @date    2015-11-10 17:22
 *
 */
var RequestMessageCallback = function (si, ri, callback, time) {
    this.si = si;
    this.ri = ri;
    this.callback = callback;
    this.time = time;
};
/**
 * @name    ResponseMessage
 * @author  GinKo
 * @mail    <a href='mailTo:yingosen@gmaiil.com'>GinKo.Wang</a>
 * @date    2015-11-10 17:28
 *
 */
var ResponseMessage = function (si, ri, date, rsp, payload, code, message) {
    this.si = si;
    this.ri = ri;
    this.date = date;
    this.rsp = rsp;
    this.payload = payload;
    this.code = code;
    this.message = message;
}
/**
 * @name    ServiceManager
 * @author  GinKo
 * @mail    <a href='mailTo:yingosen@gmaiil.com'>GinKo.Wang</a>
 * @date    2015-11-10 17:22
 *
 */
var ServiceManager = function (url, onOpenCallback, onCloseCallback, onErrorCallback) {
    this.socket = null;
    this.url = url;
    this.onOpenCallback = onOpenCallback;
    this.onCloseCallback = onCloseCallback;
    this.onErrorCallback = onErrorCallback;
    this.requests = [];
    this.callbacks = [];
    this.pushCallbacks = [];
    this.errorCallbacks = [];
    this.counter = 0;
    this.currRequest = null;
}
var logger = window.console;
ServiceManager.prototype.connect = function () {
    var _this = this;
    _this.socket = new WebSocket(this.url);
    _this.socket.onopen = function (event) {
        _this.onSocketOpen(event);
    };
    _this.socket.onmessage = function (event) {
        _this.onSocketMessage(event);
    };
    _this.socket.onerror = function (event) {
        _this.onSocketError(event);
    };
    _this.socket.onclose = function (event) {
        _this.onSocketClose(event);
    };
}
ServiceManager.prototype.close = function () {
    var _this = this;
    if (!_this.socket) {
        return;
    }
    _this.socket.close(1000, "close by client");
}
ServiceManager.prototype.invoke = function (si, params, callback, errorCallback) {
    var _this = this;
    var request = new RequestMessage(si, params);
    var requestModel = new RequestMessageModel(request, callback, errorCallback);
    if (!_this.currRequest) {
        _this.currRequest = requestModel;
        _this.sendMessage(_this.currRequest.request, _this.currRequest.callback, _this.currRequest.errorCallback);
    } else {
        _this.requests.push(requestModel);
    }
}
ServiceManager.prototype.sendMessage = function (request, callback, errorCallback) {
    var _this = this;
    if (!_this.socket) {
        logger.error("the socket is close :", this.socket);
        return;
    }
    request.date = new Date().getTime();
    request.ri = (++_this.counter) + "";
    logger.info("start " + request.si + "," + request.ri);
    if (typeof(callback) === "function") {
        _this.callbacks[request.ri] = new RequestMessageCallback(
            request.si,
            request.ri,
            callback,
            request.date
        );
    }
    if (typeof(errorCallback) === "function") {
        _this.errorCallbacks[request.ri] = errorCallback;
    }
    _this.socket.send(JSON.stringify(request));
}
ServiceManager.prototype.onSocketMessage = function (event) {
    var _this = this;
    var data = event.data;
    var resObject = JSON.parse(data);
    var message = new ResponseMessage(
        resObject.si,
        resObject.ri,
        resObject.date,
        resObject.rsp,
        resObject.payload,
        resObject.code,
        resObject.message);
    if (message.ri > 0) {
        var callback = _this.callbacks[message.ri];
        var errorCallback = _this.errorCallbacks[message.ri];
        if (message.code == 0) {
            if (callback && callback.constructor === RequestMessageCallback) {
                callback.callback.call(null, message.rsp);
            }
        } else {
            if (typeof(errorCallback) === "function") {
                errorCallback.call(null, message.code, message.message);
            } else if (typeof(_this.onErrorCallback) === "function") {
                _this.onErrorCallback.call(null, message.code, message.message);
            }
        }
        delete callback;
        delete errorCallback;
        logger.info("end " + message.si + "," + message.ri);
        _this.currRequest = _this.requests.shift();
        if (_this.currRequest && _this.currRequest.constructor === RequestMessageModel) {
            _this.sendMessage(_this.currRequest.request, _this.currRequest.callback, _this.currRequest.errorCallback);
        }
        return;
    }
    _this.pushHandler(message.si, message);
}
ServiceManager.prototype.onSocketOpen = function (event) {
    var _this = this;
    if (typeof(_this.onOpenCallback) !== "function") {
        return;
    }
    if (logger) {
        //logger.log("Connected to WebSocket server.");
    }
    _this.onOpenCallback.call(_this, event);
};
ServiceManager.prototype.onSocketError = function (event) {
    var _this = this;
    if (typeof(_this.onErrorCallback) !== "function") {
        return;
    }
    if (logger) {
        //logger.log("Connected to WebSocket occur onerror.");
    }
    _this.onErrorCallback.call(_this, event);
};
ServiceManager.prototype.onSocketClose = function (event) {
    var _this = this;
    if (typeof(_this.onCloseCallback) !== "function") {
        return;
    }
    if (logger) {
        //logger.log("Connected to WebSocket closed.");
    }
    _this.onCloseCallback.call(_this, event);
};
ServiceManager.prototype.pushHandler = function (si, payload) {
    var _this = this;
    var callbacks = _this.pushCallbacks[si];
    var len = !callbacks ? 0 : callbacks.length;
    if (len == 0) {
        return;
    }
    for (var i = 0; i < len; i++) {
        callbacks[i].call(null, payload);
    }
};
ServiceManager.prototype.addPushHandler = function (si, callback) {
    var _this = this;
    var callbacks = _this.pushCallbacks[si];
    if (!callbacks) {
        callbacks = [];
        _this.pushCallbacks[si] = callbacks;
    }
    callbacks.push(callback);
};
ServiceManager.prototype.removePushHandler = function (si, callback) {
    var _this = this;
    var callbacks = _this.pushCallbacks[si];
    var len = !callbacks ? 0 : callbacks.length;
    if (len == 0) {
        return;
    }
    for (var i = 0; i < len; i++) {
        if (callbacks[i] === callback) {
            callbacks.slice(i, 1);
            return;
        }
    }
};
